<?php

namespace App\Kuafu\Product\Service;

use App\Exceptions\BaseException;
use App\Kuafu\Base\Enum\DBEnum;
use App\Kuafu\Base\Model\BaseModel;
use App\Kuafu\Category\Model\CategoryModel;
use App\Kuafu\Category\Service\CategoryService;
use App\Kuafu\Common\Service\CommonService;
use App\Kuafu\Config\Service\ConfigService;
use App\Kuafu\Mqtt\Service\MqttQueueService;
use App\Kuafu\Mqtt\Service\MqttService;
use App\Kuafu\Product\Enum\CodeEnum;
use App\Kuafu\Product\Enum\ProductCacheEnum;
use App\Kuafu\Product\Enum\ProductCodeEnum;
use App\Kuafu\Product\Exceptions\ProductException;
use App\Kuafu\Product\Model\PackageProductMapping;
use App\Kuafu\Product\Model\Product;
use App\Kuafu\Product\Resources\ProductResource;
use App\Kuafu\ProductMenu\Exceptions\ProductMenuException;
use App\Kuafu\Shop\Enum\ShopCacheEnum;
use App\Kuafu\Shop\Enum\ShopCodeEnum;
use App\Kuafu\Shop\Exceptions\ShopException;
use App\Kuafu\Shop\Model\Shop;
use App\Kuafu\Shop\Model\ShopProductStatus;
use App\Kuafu\Shop\Service\ShopService;
use App\Kuafu\User\Exceptions\UserException;
use App\Kuafu\User\Service\AuthService;
use Exception;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;
use App\Kuafu\Base\Service\BaseService;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
use Psr\SimpleCache\InvalidArgumentException;

class ProductService extends BaseService
{
    /**
     * 新增菜品
     * @param $request
     * @param $type
     * @throws ProductException
     */
    public static function addProduct($request, $type)
    {
        //校验输入数据
        self::checkProductInputData($request);
        DB::beginTransaction();
        try {
            //组装新增数据
            $data = [
                'menuId'       => 0,
                'brandId'      => $request->header('brandId'),
                'name'         => $request->name,
                'categoryId'   => $request->categoryId,
                'desc'         => $request->desc,
                'imageUrl'     => str_replace(config('other.IMAGE_URL'), '', $request->imageUrl),
                'unit'         => $request->unit,
                'price'        => $request->price ?: 0,
                'vipPrice'     => $request->vipPrice ?: 0,
                'costPrice'    => $request->costPrice ?: 0,
                'sort'         => intval($request->sort),
                'isHot'        => $request->isHot,
                'isRecommend'  => $request->isRecommend,
                'createdAt'    => time(),
                'updatedAt'    => time(),
                'type'         => $type,
                'showPlatform' => $request->showPlatform,
                'kind'         => $request->kind,
            ];
            //新增菜品表数据
            $addProduct = Product::addProduct($data);
            if ($type == CodeEnum::PRODUCT_TYPE_PACKAGE) {
                //如果是套餐，新增关联套餐表数据
                $packageData = [];
                $productIds  = array_column($request->productList, 'productId');
                $field       = ['id', 'name', 'price', 'unit', 'imageUrl', 'categoryId'];
                $collection  = ProductService::getListByIds($productIds, $field, 0)->keyBy('id')->toArray();
                foreach ($request->productList as $key => $value) {
                    if (isset($collection[$value['productId']])) {
                        $packageData[$key]['packageId']   = $addProduct->id;
                        $packageData[$key]['productId']   = $value['productId'];
                        $packageData[$key]['num']         = $value['num'];
                        $packageData[$key]['productName'] = $collection[$value['productId']]['name'];
                        $packageData[$key]['price']       = $collection[$value['productId']]['price'];
                        $packageData[$key]['unit']        = $collection[$value['productId']]['unit'];
                        $packageData[$key]['imageUrl']    = $collection[$value['productId']]['imageUrl'];
                        $packageData[$key]['categoryId']  = $collection[$value['productId']]['categoryId'];
                    }
                }
                PackageProductMapping::addPackageProduct($packageData);
            }
            DB::commit();
            Cache::forget(ProductCacheEnum::getProductList(0));
        } catch (Exception $exception) {
            DB::rollBack();
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_INSERT, '', $exception);
        }
    }

    public static function checkPackageTotalPrice($productList, $price, $vipPrice)
    {
        $productIds = array_column($productList, 'productId');
        $field      = ['id', 'price'];
        $collection = ProductService::getListByIds($productIds, $field, 0)->keyBy('id')->toArray();
        $total      = 0;
        foreach ($productList as $value) {
            if (isset($collection[$value['productId']])) {
                $total += (int)$value['num'] * (int)$collection[$value['productId']]['price'];
            }
        }
        if ($price > $total || $vipPrice > $total) {
            throw new ProductException(ProductCodeEnum::ERR_400000, '套餐总价不能超过菜品总价');
        }
    }

    // 批量更新菜单下的菜品
    public static function batchEditProducts(int $brandId, int $menuId, array $productList): bool
    {
        $productList = collect($productList)->keyBy('productId');
        $productIds  = $productList->pluck('productId');

        // 先批量删掉没用的
        Product::query()->where('menuId', $menuId)->whereNotIn('id', $productIds)->update(['isDeleted' => 1]);

        $baseProducts = Product::query()
                               ->whereIn('id', $productIds)
                               ->where('menuId', 0)
                               ->get()
                               ->keyBy('id');

        $baseMenuProducts = Product::query()
                               ->whereIn('id', $productIds)
                               ->where('menuId', $menuId)
                               ->get()
                               ->keyBy('id');

        foreach ($productList as $v) {

            $productId   = $v['productId'];
            $baseProduct = attributeGet($baseMenuProducts, $productId, $baseProducts[$productId] ?? []);
            if (!$baseProduct) {
                throw new ProductException(ProductCodeEnum::ERR_PRODUCT_NOT_EXIST, "商品id:{$productId}不存在");
            }

            $user = AuthService::getAuthUser();

            $baseProduct['brandId']      = $brandId;
            $baseProduct['menuId']       = $menuId;
            $baseProduct['isHot']        = $v['isHot'];
            $baseProduct['isRecommend']  = $v['isRecommend'];
            $baseProduct['status']       = 0;
            $baseProduct['isDeleted']    = 0;
            $baseProduct['createdByUid'] = $user['id'];
            $baseProduct['updatedByUid'] = $user['id'];


            unset($baseProduct['createdAt'], $baseProduct['updatedAt']);

            $product = Product::checkProductId($brandId, $menuId, $productId);
            if (empty($product)) {
                $baseProduct['categoryId'] = CategoryService::getMenuCategoryIdByPid($baseProduct['categoryId'],
                    $menuId);
            } else {
                unset($baseProduct['categoryId']);
            }

            if (($v['name'] ?? '')) {
                $baseProduct['name'] = $v['name'];
            }

            if ($v['price']) {
                $baseProduct['price'] = $v['price'];
            }

            if (isset($v['costPrice']) && $v['costPrice']) {
                $baseProduct['costPrice'] = $v['costPrice'];
            }

            if (isset($v['vipPrice']) && $v['vipPrice']) {
                $baseProduct['vipPrice'] = $v['vipPrice'];
            }

            if (isset($v['sort']) && is_int($v['sort'])) {
                $baseProduct['sort'] = $v['sort'];
            }

            if (isset($v['desc']) && strlen(trim($v["desc"])) > 0) {
                $baseProduct['desc'] = $v['desc'];
            }

            if (isset($v['imageUrl']) && strlen(trim($v["imageUrl"])) > 0) {
                $baseProduct['imageUrl'] = getPicturePath($v['imageUrl']);
            }

            if (isset($v['categoryId']) && is_int($v['categoryId']) && $v['categoryId'] > 0) {
                $baseProduct['categoryId'] = $v['categoryId'];
            }

            if (isset($v['showPlatform']) && is_int($v['showPlatform']) && $v['showPlatform'] >= 0) {
                $baseProduct['showPlatform'] = $v['showPlatform'];
            }

            $newProduct              = $baseProduct->toArray();
            $newProduct['updatedAt'] = time();

            Product::setProductByMenuIdPid($menuId, $productId, $newProduct);
        }
        Cache::forget(ProductCacheEnum::getProductList($menuId));

        return true;
    }

    // 批量新增菜单下的菜品
    public static function batchAddProducts(int $menuId, array $productList): bool
    {
        $productList  = collect($productList)->keyBy('productId');
        $baseProducts = Product::query()
                               ->whereIn('id', $productList->pluck('productId'))
                               ->where('menuId', 0)
                               ->get()
                               ->keyBy('id')
                               ->toArray();

        $insertData = [];
        foreach ($productList as $v) {
            $productId   = $v['productId'];
            $baseProduct = $baseProducts[$productId] ?? [];
            if (!$baseProduct) {
                throw new ProductException(ProductCodeEnum::ERR_PRODUCT_NOT_EXIST, "商品id:{$productId}不存在");
            }
            $user                        = AuthService::getAuthUser();
            $baseProduct['id']           = $productId;
            $baseProduct['menuId']       = $menuId;
            $baseProduct['isHot']        = $v['isHot'];
            $baseProduct['isRecommend']  = $v['isRecommend'];
            $baseProduct['status']       = 0;
            $baseProduct['isDeleted']    = 0;
            $baseProduct['createdAt']    = time();
            $baseProduct['createdByUid'] = $user['id'];
            $baseProduct['updatedAt']    = time();
            $baseProduct['updatedByUid'] = $user['id'];

            if (isset($v['categoryId']) && is_int($v['categoryId']) && $v['categoryId'] > 0) {
                $categoryModel = CategoryModel::query()
                                              ->where('id', $v['categoryId'])
                                              ->select(['id', 'pid'])
                                              ->first();

                $baseProduct['categoryId']   = CategoryService::getMenuCategoryIdByPid(
                    $categoryModel->pid > 0 ? $categoryModel->pid : $v['categoryId'],
                $menuId
            );
            }else{
                $baseProduct['categoryId']   = CategoryService::getMenuCategoryIdByPid($baseProduct['categoryId'], $menuId);
            }

            if (($v['name'] ?? '')) {
                $baseProduct['name'] = $v['name'];
            }
            if ($v['price']) {
                $baseProduct['price'] = $v['price'];
            }

            if (isset($v['costPrice']) && $v['costPrice']) {
                $baseProduct['costPrice'] = $v['costPrice'];
            }

            if (isset($v['vipPrice']) && $v['vipPrice']) {
                $baseProduct['vipPrice'] = $v['vipPrice'];
            }

            if (isset($v['sort']) && is_int($v['sort'])) {
                $baseProduct['sort'] = $v['sort'];
            }

            if (isset($v['desc']) && strlen(trim($v["desc"])) > 0) {
                $baseProduct['desc'] = $v['desc'];
            }

            if (isset($v['imageUrl']) && strlen(trim($v["imageUrl"])) > 0) {
                $baseProduct['imageUrl'] = getPicturePath($v['imageUrl']);
            }

            if (isset($v['showPlatform']) && is_int($v['showPlatform']) && $v['showPlatform'] >= 0) {
                $baseProduct['showPlatform'] = $v['showPlatform'];
            }

            $insertData[] = $baseProduct;
        }
        try {
            Product::query()->insert($insertData);

            Cache::forget(ProductCacheEnum::getProductList($menuId));
        } catch (Exception $e) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_INSERT, '', $e);
        }

        return true;
    }

    /**
     * 获取菜品列表
     * @param $brandId
     * @param $categoryId
     * @param $name
     * @param $pageSize
     * @param $pageNum
     * @param $menuId
     * @param int $kind
     * @return array
     */
    public static function getProductList(
        $brandId,
        $categoryId,
        $name,
        $pageSize,
        $pageNum,
        $menuId,
        int $kind = 0
    ): array {
        $query = Product::getProductDataPage($brandId, $categoryId, $name, $pageSize, $pageNum, $menuId, $kind);
        //获取分类ids
        $cateIds = array_column($query->items(), 'categoryId');
        //查询分类ids对应分类数据
        $collection = CategoryService::getCateListByIds($cateIds);
        $cateArr    = $collection->keyBy("id")->toArray();
        $returnData = [];
        foreach ($query->items() as $k => $v) {
            $returnData[$k]['id']          = $v->id;
            $returnData[$k]['name']        = $v->name;
            $returnData[$k]['kind']        = $v->kind;
            $returnData[$k]['kindText']    = CodeEnum::KIND_MAP[$v->kind] ?? "-";
            $returnData[$k]['isDeleted']   = $v->isDeleted;
            $returnData[$k]['imageUrl']    = picturePath($v->imageUrl);
            $returnData[$k]['unit']        = $v->unit;
            $returnData[$k]['price']       = $v->price;
            $returnData[$k]['vipPrice']    = $v->price;
            $returnData[$k]['costPrice']   = $v->price;
            $returnData[$k]['status']      = $v->status;
            $returnData[$k]['isHot']       = $v->isHot;
            $returnData[$k]['isRecommend'] = $v->isRecommend;
            $returnData[$k]['type']        = $v->type;
            $returnData[$k]['kind']        = $v->kind;
            if (key_exists($v->categoryId, $cateArr)) {
                $returnData[$k]['category']['id']   = $cateArr[$v->categoryId]['id'];
                $returnData[$k]['category']['name'] = $cateArr[$v->categoryId]['name'];
            } else {
                $returnData[$k]['category'] = (object)[];
            }
        }
        return [
            'pageNum'  => $query->currentPage(),
            'pageSize' => $query->perPage(),
            'count'    => $query->total(),
            'lists'    => $returnData,
        ];
    }

    /**
     * 根据id获取菜品详情
     * @param $productId
     * @return mixed
     */
    public static function getProductInfoById($productId)
    {
        $info = Product::getProductInfoById($productId);
        if (!empty($info)) {
            $info               = $info->toArray();
            $productIds         = array_column($info['product_package_rel'], 'productId');
            $field              = ['name', 'price', 'id', 'unit'];
            $collection         = ProductService::getListByIds($productIds, $field, 0);
            $productList        = $collection->keyBy('id')->toArray();
            $packageList        = $info['product_package_rel'];
            $showArr            = [
                CodeEnum::PRODUCT_SHOW_APPLET,
                CodeEnum::PRODUCT_SHOW_POS
            ];
            $info['showPos']    = false;
            $info['showApplet'] = false;
            $decodeRules        = CommonService::decodeRules($info['showPlatform'], $showArr);
            if (in_array(CodeEnum::PRODUCT_SHOW_POS, $decodeRules)) {
                $info['showPos'] = true;
            }
            if (in_array(CodeEnum::PRODUCT_SHOW_APPLET, $decodeRules)) {
                $info['showApplet'] = true;
            }
            foreach ($packageList as $key => $value) {
                if (isset($productList[$value['productId']])) {
                    $packageList[$key]['name']  = $productList[$value['productId']]['name'];
                    $packageList[$key]['price'] = $productList[$value['productId']]['price'];
                    $packageList[$key]['unit']  = $productList[$value['productId']]['unit'];
                }
            }
            unset($info['product_package_rel']);
            $info['productList'] = $packageList;
        }
        return $info;
    }


    /**
     * 删除菜品（软删除）
     */
    public static function deletedProduct($productId)
    {
        //获取菜品基本信息
        $info = self::getProductInfoById($productId);
        if (empty($info)) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_NOT_EXIST);
        }
        //获取当前登录账户信息
        $user = AuthService::getAuthUser();
        $data = [
            'isDeleted' => 1,
            'updatedBy' => $user['username'],
            'updatedAt' => time(),
        ];
        // 删除底层菜品
        $menuId = 0;
        $res    = Product::updateProduct($productId, $menuId, $data);
        if (!$res) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_UPDATE);
        }
        if ($info['type'] == CodeEnum::PRODUCT_TYPE_PACKAGE) {
            PackageProductService::delPackageById($productId, $menuId);
        }
        Cache::forget(ProductCacheEnum::getProductList($menuId));
    }

    /**
     * 根据id修改菜品信息
     * @param $request
     * @param $type
     * @throws ProductException
     * @throws UserException
     */
    public static function setProductById($request, $type)
    {
        $res = self::getProductInfoById($request->productId);
        if (empty($res)) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_NOT_EXIST);
        }
        //校验输入数据
        self::checkProductInputData($request);
        //获取当前登录账户信息
        $user = AuthService::getAuthUser();
        $data = [
            'name'         => $request->name,
            'categoryId'   => $request->categoryId,
            'desc'         => $request->desc,
            'imageUrl'     => str_replace(config("other.IMAGE_URL"), '', $request->imageUrl),
            'unit'         => $request->unit,
            'price'        => $request->price ?: 0,
            'vipPrice'     => $request->vipPrice ?: 0,
            'costPrice'    => $request->costPrice ?: 0,
            'sort'         => intval($request->sort),
            'updatedBy'    => $user['username'],
            'updatedAt'    => time(),
            'isHot'        => $request->isHot,
            'isRecommend'  => $request->isRecommend,
            'type'         => $type,
            'showPlatform' => $request->showPlatform ?: 3
        ];
        //修改底层菜品
        $menuId = 0;
        DB::beginTransaction();
        try {
            Product::updateProduct($request->productId, $menuId, $data);
            if ($type == CodeEnum::PRODUCT_TYPE_PACKAGE) {
                //如果是套餐
                //先删除原有底层数据
                PackageProductMapping::delPackageProductById($request->productId, $menuId);
                //组装新的套餐商品信息
                $packageData = [];
                $productIds  = array_column($request->productList, 'productId');
                $field       = ['id', 'name', 'price', 'unit', 'imageUrl', 'categoryId'];
                $collection  = ProductService::getListByIds($productIds, $field, 0)->keyBy('id')->toArray();
                foreach ($request->productList as $key => $value) {
                    if (isset($collection[$value['productId']])) {
                        $packageData[$key]['packageId']   = $request->productId;
                        $packageData[$key]['num']         = $value['num'];
                        $packageData[$key]['productId']   = $value['productId'];
                        $packageData[$key]['price']       = $collection[$value['productId']]['price'];
                        $packageData[$key]['productName'] = $collection[$value['productId']]['name'];
                        $packageData[$key]['imageUrl']    = $collection[$value['productId']]['imageUrl'];
                        $packageData[$key]['unit']        = $collection[$value['productId']]['unit'];
                        $packageData[$key]['categoryId']  = $collection[$value['productId']]['categoryId'];
                    }
                }
                PackageProductMapping::addPackageProduct($packageData);
            }
            Cache::forget(ProductCacheEnum::getProductList($menuId));
            DB::commit();
        } catch (Exception $exception) {
            DB::rollBack();
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_MODIFY, '', $exception);
        }

    }

    /**
     * 菜品上下架 0 上架 1下架 2其他
     * @param $request
     * @throws ProductException
     */
    public static function setProductStatus($request)
    {
        $res = self::getProductInfoById($request->productId);
        if (empty($res)) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_NOT_EXIST);
        }
        if ($res['status'] == $request->status) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_STATUS_EXIST);
        }
        //修改底层菜品
        $menuId = 0;
        Product::updateProduct($request->productId, $menuId, ['status' => $request->status]);

        Cache::forget(ProductCacheEnum::getProductList($menuId));
    }

    /**
     * 菜品热门 0 不是 1是
     * @param $request
     * @throws ProductException
     */
    public static function setProductIsHot($request)
    {
        $res = self::getProductInfoById($request->productId);
        if (empty($res)) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_NOT_EXIST);
        }
        if ($res['isHot'] == $request->isHot) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_STATUS_EXIST);
        }
        // 修改底层菜品信息
        Product::updateProduct($request->productId, 0, ['isHot' => $request->isHot]);

        Cache::forget(ProductCacheEnum::getProductList($res->menuId));
    }

    /**
     * 菜品推荐 0 不是 1是
     * @param $request
     * @throws ProductException
     */
    public static function setProductIsRecommend($request)
    {

        $res = self::getProductInfoById($request->productId);
        if (empty($res)) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_NOT_EXIST);
        }
        if ($res['isRecommend'] == $request->isRecommend) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_STATUS_EXIST);
        }
        // 修改底层菜品信息
        Product::updateProduct($request->productId, 0, ['isRecommend' => $request->isRecommend]);

        Cache::forget(ProductCacheEnum::getProductList($res->menuId));
    }

    /**
     * @throws ProductException
     */
    public static function checkProductInputData($request)
    {
        if (!is_numeric($request->price) || $request->price <= 0) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_PRICE);
        }
        self::checkNumberPoint($request->price, 2);
        if (!empty($request->vipPrice) && (!is_numeric($request->price) || $request->price <= 0)) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_PRICE);
        }
        self::checkNumberPoint($request->vipPrice, 2);
        if (!empty($request->costPrice) && (!is_numeric($request->costPrice) || $request->costPrice <= 0)) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_PRICE);
        }
        self::checkNumberPoint($request->costPrice, 2);
    }

    /**
     * @param $productIds
     * @param $productMenuId
     * @throws ProductException
     */
    public static function bindMenuIds($productIds, $productMenuId): void
    {
        $res = Product::bindMenuIds($productIds, $productMenuId);
        if (!$res) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_BIND_MENU);
        }

        Cache::forget(ProductCacheEnum::getProductList($productMenuId));
    }

    public static function checkNumberPoint($num, int $position)
    {
        $len = strlen(substr(strrchr($num, "."), 1));
        if ($len > $position) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_PRICE_TWO_POS);
        }
    }

    public static function getListByIds($ids, $field, $menuId)
    {
        // TODO 有隐患
        return Product::query()
                      ->whereIn('id', $ids)
                      ->where('menuId', $menuId)
                      ->when(!empty($field), function ($query) use ($field) {
                          $query->select($field);
                      })
                      ->get();
    }

    /**
     * 查询店铺下的所有菜，包括异常状态的
     * @param int $shopId
     * @return Builder[]|Collection
     * @throws ShopException
     */
    public static function getShopProducts(int $shopId)
    {
        $shop = ShopService::getShopInfo($shopId);
        if (!$shop) {
            throw new ShopException(ShopCodeEnum::ERR_SHOP_ID_NO_EXIST);
        }

        $menuId = $shop->menuId;
        if (!$menuId) {
            throw new ShopException(ShopCodeEnum::ERR_SHOP_NULL_MENU);
        }

        return Product::query()->where('menuId', $menuId)->select('id', 'name', 'categoryId')->get();
    }


    public static function productConfig()
    {
        try {

            return [
                "category" => CategoryModel::query()->where
            ];

        } catch (Exception $exception) {
            throw new ShopException(ShopCodeEnum::FAIL_GET_SHOP_PRODUCT_CONFIG);
        }
    }


    public static function productLists(int $kind, int $categoryId)
    {

    }

    /**
     * 添加菜品
     *
     * @param int $shopId
     * @param int $brandId
     * @param object $params
     * @throws ProductException
     */
    public static function insert(int $shopId, int $brandId, object $params)
    {
        try {
            DB::beginTransaction();

            $requestFrom = getRequestFrom();
            $menuId      = 0;
            if (in_array($requestFrom, ["pos", "merchant"])) {
                $menuId               = Shop::query()->where("id", $shopId)->first()->menuId;
                $params->showPlatform = $params->showPlatform ? CodeEnum::PRODUCT_SHOW_APPLET + CodeEnum::PRODUCT_SHOW_POS : CodeEnum::PRODUCT_SHOW_POS;
            } else {
                if ($params->type == CodeEnum::PRODUCT_TYPE_PACKAGE && $params->kind == CodeEnum::KIND_CUSTOMER) {
                    throw new ProductException(ProductCodeEnum::ERR_PRODUCT_WEB_CUSTOM_PACKAGE_ADD);
                }
            }

            $productId = Product::query()->insertGetId(
                collect((array)$params)->except(["productList"])
                                       ->merge(property_exists($params, "imageUrl") ? [
                                           "imageUrl" => getPicturePath($params->imageUrl)
                                       ] : [])
                                       ->merge([
                                           "brandId"   => $brandId,
                                           "createdAt" => time(),
                                           "updatedAt" => time()
                                       ])->toArray()
            );

            if ($params->type == CodeEnum::PRODUCT_TYPE_PACKAGE) {
                Product::insertOrUpdateChildProducts(
                    $shopId,
                    $brandId,
                    $productId,
                    $params->productList,
                    $params->price,
                    $menuId
                );
            }

            DB::commit();

            if (in_array(getRequestFrom(), ["merchant", "pos"])) {
                Cache::forget(ShopCacheEnum::getCustomShopProductStatus($shopId));
                Product::refreshGetMenuProductsPackageCustom($shopId);

                MqttQueueService::updateProductMenu([$shopId]);

            } else {
                Cache::forget(ProductCacheEnum::getProductList(0));
                Product::refreshGetMenuProductsCustom();
            }
        } catch (ProductException $productException) {
            DB::rollBack();
            throw $productException;
        } catch (Exception $exception) {
            DB::rollBack();
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_INSERT, '', $exception);
        }
    }

    /**
     * @param bool $show
     * @param $showPlatform
     * @return int
     */
    public static function getShowPlatformByApplet(bool $show, $showPlatform): int
    {
        list($showPos, $showApplet) = ProductResource::getClientShow($showPlatform);
        if ($showApplet) {
            if (!$show) {
                $nowShowPlatform = $showPlatform - CodeEnum::PRODUCT_SHOW_APPLET;
            } else {
                $nowShowPlatform = $showPlatform;
            }
        } else {
            if ($show) {
                $nowShowPlatform = $showPlatform + CodeEnum::PRODUCT_SHOW_APPLET;
            } else {
                $nowShowPlatform = $showPlatform;
            }
        }
        return $nowShowPlatform;
    }

    /**
     * 编辑菜品
     *
     * @param int $shopId
     * @param int $brandId
     * @param object $params
     * @throws ProductException
     */
    public static function update(int $shopId, int $brandId, object $params)
    {
        try {
            DB::beginTransaction();

            $product = Product::query()->where("id", $params->id)->where("menuId", 0)->first();

            if (empty($params->isMust)) {
                $params->isMust = BaseModel::SWITCH_OFF;
            }

            switch (getRequestFrom()) {
                case "merchant":
                case "pos":

                    $menuId = Shop::query()->where("id", $shopId)->first()->menuId;
                    if ($product->kind == CodeEnum::KIND_NORMAL) {
                        $product = Product::query()->where("id", $params->id)->where("menuId", $menuId)->first();
                    }

                    if ($params->isMust == BaseModel::SWITCH_ON) {
                        $list = ShopProductStatus::query()
                                                 ->with('productRel', function ($query) {
                                                     $query->where('status', 0)->where('menuId', 0);
                                                 })
                                                 ->where('isMust', BaseModel::SWITCH_ON)
                                                 ->where('shopId', $shopId)
                                                 ->where('productId', '<>', $params->id)
                                                 ->get()
                                                 ->filter(function ($item) {
                                                     return !empty($item->productRel);
                                                 });

                        $count    = $list->count();
                        $maxCount = ConfigService::getMustProductMaxCount();
                        if ($count >= $maxCount) {
                            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_PRODUCT_NOT_IN_SHOP,
                                '超过最大必点数' . $maxCount . '个');
                        }
                    }

                    $shopProductStatus = ShopProductStatus::insertOrGet(
                        $shopId, $brandId, $params->id,
                        0, $product->showPlatform,
                        $product->kind == CodeEnum::KIND_CUSTOMER ? DBEnum::STATUS_DOWN : DBEnum::STATUS_ON
                    );

                    if (!(bool)$params->showPlatform) {
                        $params->isMust = BaseModel::SWITCH_OFF;
                    }
                    $params->showPlatform = self::getShowPlatformByApplet(
                        (bool)$params->showPlatform,
                        $shopProductStatus->showPlatform
                    );


                    switch ($product->kind) {
                        case CodeEnum::KIND_CUSTOMER:
                            switch ($product->type) {
                                case CodeEnum::PRODUCT_TYPE_SINGLE:
                                    //自定义菜品：可以修改菜品售价、上架/下架、点餐小程序展示开关，单位不可编辑。
                                    //新下发的自定义菜品默认下架状态，需要加盟商手动上架。
                                    $shopProductStatus->update(collect((array)$params)->except(["id"])->toArray());
                                    break;
                                case CodeEnum::PRODUCT_TYPE_PACKAGE:
                                    //自定义套餐：可以修改套餐名称、售价、单位、上架/下架、点餐小程序展示开关，分类默认 套餐 分类不可编辑，图片默认且不可编辑。
                                    if ($product->shopId != $shopId) {
                                        throw new ProductException(ProductCodeEnum::ERR_PRODUCT_PRODUCT_NOT_IN_SHOP);
                                    }
                                    $shopProductStatus->update(
                                        collect((array)$params)
                                            ->except(["name", "unit", "id", "productList"])
                                            ->toArray()
                                    );

                                    $productUpdate = [
                                        'name' => $params->name,
                                        'unit' => $params->unit
                                    ];
                                    if (isset($params->imageUrl) && $params->imageUrl) {
                                        $productUpdate['imageUrl'] = getPicturePath($params->imageUrl);
                                    }
                                    $product->update($productUpdate);

                                    Product::insertOrUpdateChildProducts(
                                        $shopId,
                                        $brandId,
                                        $params->id,
                                        $params->productList,
                                        $params->price,
                                        $menuId
                                    );
                                    break;
                            }
                            break;
                        case CodeEnum::KIND_NORMAL:
                            //标准菜品：可以修改上架/下架、点餐小程序展示开关。新下发的标准商品默认上架状态。
                            $shopProductStatus->update(collect((array)$params)->except(["id"])->toArray());
                            break;
                    }

                    Cache::forget(ShopCacheEnum::getCustomShopProductStatus($shopId));
                    Product::refreshGetMenuProductsPackageCustom($shopId);

                MqttQueueService::updateProductMenu([$shopId]);


                break;
                case "web":
                    // Product 没有 status 字段
                    unset($params->isMust);
                    Product::query()->where("id", $params->id)
                           ->where("menuId", 0)
                           ->update(
                               collect((array)$params)->except(["productList"])
                                                      ->merge(property_exists($params, "imageUrl") ? [
                                                          "imageUrl" => getPicturePath($params->imageUrl)
                                                      ] : [])
                                                      ->merge([
                                                          "brandId"   => $brandId,
                                                          "updatedAt" => time()
                                                      ])->toArray()
                           );

                    if ($product->kind == CodeEnum::KIND_CUSTOMER && $product->type == CodeEnum::PRODUCT_TYPE_SINGLE) {
                        ShopProductStatus::query()
                                         ->where("productId", $params->id)
                                         ->where("status", DBEnum::STATUS_DOWN)
                                         ->update([
                                             "price"        => $params->price,
                                             "showPlatform" => $params->showPlatform
                                         ]);
                    }

                    if ($params->type == CodeEnum::PRODUCT_TYPE_PACKAGE) {
                        Product::insertOrUpdateChildProducts(
                            $shopId,
                            $brandId,
                            $params->id,
                            $params->productList,
                            $params->price
                        );
                    }
                    break;
            }

            DB::commit();
            Cache::forget(ProductCacheEnum::getProductList(0));
            Product::refreshGetMenuProductsCustom();
        } catch (ProductException $productException) {
            DB::rollBack();
            throw $productException;
        } catch (Exception $exception) {
            DB::rollBack();
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_MODIFY, '', $exception);
        }
    }

    /**
     * 菜品详情
     *
     * @param int $id
     * @param int $shopId
     * @param int $brandId
     * @param object|null $params
     * @return array
     * @throws ProductException
     */
    public static function detail(int $id, int $shopId, int $brandId, object $params = null): array
    {
        try {
            $productTemplate = Product::query()->where("id", $id)->first();
            $menuId          = attributeGet($params, "menuId", 0);
            if (in_array(getRequestFrom(), ["pos", "merchant"])) {
                if ($productTemplate->kind == CodeEnum::KIND_NORMAL) {
                    $menuId = Shop::query()->where("id", $shopId)->first()->menuId;
                }
            }

            $product = Product::query()->where("id", $id)->where("menuId", $menuId)->with([
                "productPackageRel" => function ($query) use ($menuId, $productTemplate) {
                    if ($productTemplate->kind == CodeEnum::KIND_CUSTOMER && $productTemplate->type == CodeEnum::PRODUCT_TYPE_PACKAGE) {
                        return $query;
                    } else {
                        $query->where("menuId", $menuId);
                    }
                },
                "productStatusRel"  => function ($query) use ($shopId, $brandId) {
                    $query->where("shopId", $shopId)->where("brandId", $brandId);
                },
                "categoryRel"
            ])->first();

            return [
                "product" => $product ? ProductResource::collection([$product])->getIterator()
                                                       ->current() : new \stdClass()
            ];
        } catch (Exception $exception) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_GET, "获取菜品详情失败", $exception);
        }
    }


    /**
     * 添加套餐弹出菜品列表
     *
     * @param int $shopId
     * @param int $brandId
     * @return array
     * @throws ProductException
     */
    public static function ordinaryProducts(int $shopId, int $brandId): array
    {
        try {
            $menuId = Shop::query()->where("id", $shopId)->first()->menuId;
            return [
                "product" => ProductResource::collection(Product::getChildProductLists($shopId, $brandId, $menuId))
            ];
        } catch (Exception $exception) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_GET);
        }
    }

    /**
     * 菜品状态 修改
     *
     * @param int $shopId
     * @param int $brandId
     * @param object $params
     * @throws BaseException
     * @throws ProductException
     * @throws ProductMenuException
     * @throws ShopException
     */
    public static function webStatus(int $shopId, int $brandId, object $params)
    {
        try {
            $productTemplate = Product::query()->where("id", $params->id)->first();

            if ($params->status == DBEnum::STATUS_DOWN) {
                if ($productTemplate->type == CodeEnum::PRODUCT_TYPE_SINGLE) {
                    $existsPackage = Product::query()->where("type", CodeEnum::PRODUCT_TYPE_PACKAGE)
                                            ->whereHas("productPackageRel", function ($query) use ($params) {
                                                $query->where("productId", $params->id);
                                            })->count();
                    if ($existsPackage) {
                        throw new ProductException(ProductCodeEnum::ERR_PRODUCT_EXISTS_IN_PACKAGE);
                    }
                }

                ShopProductStatus::query()->where("productId", $params->id)->update([
                    "status" => $params->status
                ]);
            }

            if ($productTemplate->kind == CodeEnum::KIND_CUSTOMER) {
                if ($productTemplate->type == CodeEnum::PRODUCT_TYPE_SINGLE) {
                    //单菜品清空所有缓存数据
                    $keys = Redis::keys("shopCustomProductNum:shopId*");
                    if (!empty($keys)) {
                        Redis::del($keys);
                    }

                    // 清空自定义库的缓存
                    Product::refreshGetMenuProductsCustom();
                }
            }

            Product::query()
                   ->where("id", $params->id)
                   ->where("menuId", 0)
                   ->update([
                       "status" => $params->status
                   ]);

            Cache::forget(ProductCacheEnum::getProductList(0));
        } catch (BaseException $exception) {
            throw $exception;
        } catch (Exception $exception) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_STATUS_SET);
        }
    }

    /**
     * 商家端状态修改
     *
     * @param int $shopId
     * @param int $brandId
     * @param object $params
     * @throws BaseException
     * @throws ProductException
     * @throws ShopException|InvalidArgumentException
     */
    public static function merchantStatus(int $shopId, int $brandId, object $params)
    {
        try {
            $productTemplate = Product::query()->where("id", $params->id)->first();

            if ($productTemplate->kind != CodeEnum::KIND_CUSTOMER) {
                $product = self::shopStandardProductValidate($shopId, $brandId, $params->id);
                ShopProductStatus::status(
                    $shopId, $brandId, $productTemplate->id, 0,
                    $product->showPlatform, $params->status
                );
            } else {
                $existOnlineCustomProducts = self::getExistsCustomProductNum($shopId, $brandId);
                if (
                    $params->status == DBEnum::STATUS_ON
                    &&
                    $existOnlineCustomProducts >= ConfigService::getCustomProductMaxOnlineCount()
                ) {
                    throw new ProductException(ProductCodeEnum::CUSTOMER_PRODUCT_LIMIT);
                }
//                $cacheKey = ProductCacheEnum::getShopCustomProductNumCacheKey($shopId);
//                Cache::store("redis")->increment($cacheKey, $params->status == DBEnum::STATUS_ON ? 1 : -1);
                ShopProductStatus::status(
                    $shopId, $brandId, $productTemplate->id, $productTemplate->price,
                    $productTemplate->showPlatform, $params->status
                );
            }

            Cache::forget(ShopCacheEnum::getCustomShopProductStatus($shopId));

            MqttQueueService::updateProductMenu([$shopId]);

        } catch (BaseException $exception) {
            throw $exception;
        } catch (Exception $exception) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_STATUS_SET);
        }
    }

    /**
     * pos端状态修改
     *
     * @param int $shopId
     * @param int $brandId
     * @param object $params
     * @throws BaseException
     * @throws ProductException
     * @throws ShopException|InvalidArgumentException
     */
    public static function posStatus(int $shopId, int $brandId, object $params)
    {
        try {
            $productTemplate   = Product::query()->where("id", $params->id)->first();
            $shopProductStatus = ShopProductStatus::insertOrGet(
                $shopId,
                $brandId,
                $params->id,
                $productTemplate->price,
                $productTemplate->showPlatform
            );

            if (property_exists($params, "status")) {
                $shopProductStatus->status = $params->status;
            }

            if (property_exists($params, "out")) {
                $shopProductStatus->out = $params->out;
            }

            if ($productTemplate->kind != CodeEnum::KIND_CUSTOMER) {
                   $product                         = self::shopStandardProductValidate($shopId, $brandId, $params->id);
                $shopProductStatus->price        = 0;
//                $shopProductStatus->showPlatform = $product->showPlatform;
                $shopProductStatus->save();
            } else {
                if (property_exists($params, "status")) {
                    $existOnlineCustomProducts = self::getExistsCustomProductNum($shopId, $brandId);
                    if (
                        $params->status == DBEnum::STATUS_ON
                        &&
                        $existOnlineCustomProducts >= ConfigService::getCustomProductMaxOnlineCount()
                    ) {
                        throw new ProductException(ProductCodeEnum::CUSTOMER_PRODUCT_LIMIT);
                    }
//                    $cacheKey = ProductCacheEnum::getShopCustomProductNumCacheKey($shopId);
//                    Cache::store("redis")->increment($cacheKey, $params->status == DBEnum::STATUS_ON ? 1 : -1);
                }
                $shopProductStatus->save();
            }

            // 清除定制状态缓存 key
            Cache::forget(ShopCacheEnum::getCustomShopProductStatus($shopId));
            // 重新拉定制状态缓存
            ShopService::sGetCustomStatusList($shopId);
            // 自定义套餐
            Product::refreshGetMenuProductsPackageCustom($shopId);
            // 更新 mqtt
            MqttQueueService::updateProductMenu([$shopId]);
        } catch (BaseException $exception) {
            throw $exception;
        } catch (Exception $exception) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_STATUS_SET);
        }
    }

    /**
     * @param int $shopId
     * @param int $brandId
     * @param object $params
     * @throws BaseException
     * @throws ProductException
     * @throws UserException
     */
    public static function delete(int $shopId, int $brandId, object $params)
    {
        try {
            $product     = Product::query()->where("id", $params->id)->first();
            $requestFrom = getRequestFrom();
            switch ($requestFrom) {
                case "pos":
                case "merchant":
                    if ($product->type == CodeEnum::PRODUCT_TYPE_PACKAGE && $product->kind == CodeEnum::KIND_CUSTOMER) {
                        ShopProductStatus::del(
                            $shopId,
                            $brandId,
                            $params->id,
                            $product->price,
                            $product->showPlatform,
                            $product->status
                        );

                        $product->update(["isDeleted" => 1]);
                    } else {
                        throw new ProductException(ProductCodeEnum::ERR_PRODUCT_PACKAGE_DELETED);
                    }

                    Cache::forget(ShopCacheEnum::getCustomShopProductStatus($shopId));
                    Product::refreshGetMenuProductsPackageCustom($shopId);

                    MqttQueueService::updateProductMenu([$shopId]);

                    break;
                case "web":
                    //获取当前登录账户信息
                    $user = AuthService::getAuthUser();
                    // 删除底层菜品
                    $menuId = 0;
                    $res    = Product::updateProduct(
                        $params->id,
                        $menuId,
                        [
                            'isDeleted' => 1,
                            'updatedBy' => $user['username'],
                            'updatedAt' => time(),
                        ]);
                    if (!$res) {
                        throw new ProductException(ProductCodeEnum::ERR_PRODUCT_UPDATE);
                    }
                    if ($product->type == CodeEnum::PRODUCT_TYPE_PACKAGE) {
                        PackageProductService::delPackageById($params->id, $menuId);
                    }

                    Cache::forget(ProductCacheEnum::getProductList($menuId));
                    Product::refreshGetMenuProductsCustom();
                    break;
            }


        } catch (BaseException $exception) {
            throw $exception;
        } catch (Exception $exception) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_PACKAGE_DELETED);
        }
    }


    /**
     * 获取已上架自定菜品数量
     *
     * @param int $shopId
     * @param int $brandId
     * @return int
     * @throws InvalidArgumentException
     */

    public static function getExistsCustomProductNum(int $shopId, int $brandId): int
    {
//        $cacheKey                  = ProductCacheEnum::getShopCustomProductNumCacheKey($shopId);
//        $existOnlineCustomProducts = Cache::get($cacheKey);
//        if (is_null($existOnlineCustomProducts)) {
            $existOnlineCustomProducts = ShopProductStatus::getShopCustomProductOnlineNum($shopId, $brandId);
//            Cache::store("redis")->set($cacheKey, $existOnlineCustomProducts, 5 * 60);
//        }
        return (int)$existOnlineCustomProducts;
    }


    /**
     * 门店标准菜品校验
     *
     * @param int $shopId
     * @param int $brandId
     * @param int $productId
     * @return Builder|Model|object
     * @throws ProductException
     * @throws ShopException
     */
    public static function shopStandardProductValidate(int $shopId, int $brandId, int $productId)
    {
        $menuId = (int)(($shop = ShopService::getShopInfo($shopId)) ? $shop->menuId : 0);
        if (!($menuId > 0)) {
            throw new ShopException(ShopCodeEnum::ERR_SHOP_NO_MENU);
        }

        $product = Product::checkProductId($brandId, $menuId, $productId);
        if (!$product) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_PRODUCT_NOT_IN_SHOP);
        }
        return $product;
    }


    /**
     * 查询菜品不同门店价格数据
     * @param int $productId
     * @return array
     */
    public static function getPriceData(int $productId): array
    {
        $priceData = [];
        Product::query()
               ->with(['categoryRel'])
               ->where('id', $productId)
               ->where('status', DBEnum::STATUS_ON)
               ->orderByDesc('menuId')
               ->chunk(100, function ($product) use (&$priceData) {
                   foreach ($product as $v) {
                       if (isset($priceData[$v->price])) {
                           continue;
                       }
                       $priceData[$v->price] = [
                           'id'           => $v->id,//菜品id
                           'categoryName' => $v->categoryRel->name ?? "",//菜品分类名称
                           'price'        => $v->price,//菜品价格，分
                           'name'         => $v->name,//菜品名称
                           'img'          => '',
                       ];
                   }
               });
        if ($priceData) {
            $priceData = array_values($priceData);
        }
        return array_merge([
            [
                'id'           => $productId,
                'price'        => -1,//菜品价格，分
                'name'         => '默认',//菜品名称
                'categoryName' => '',//菜品名称
                'img'          => '',
            ]
        ], $priceData);
    }

    /**
     * 菜品详情 -抖音小程序
     * @param int $id
     * @param int $shopId
     * @param int $brandId
     * @return array
     * @throws ProductException
     */
    public static function detailFromApplet(int $id, int $shopId, int $brandId): array
    {
        try {
            $productTemplate = Product::query()->where("id", $id)->first();
            $menuId          = Shop::query()->where("id", $shopId)->first()->menuId;
            $product         = Product::query()->where("id", $id)->where("menuId", $menuId)->with([
                "productPackageRel" => function ($query) use ($menuId, $productTemplate) {
                    if ($productTemplate->kind == CodeEnum::KIND_CUSTOMER && $productTemplate->type == CodeEnum::PRODUCT_TYPE_PACKAGE) {
                        return $query;
                    } else {
                        $query->where("menuId", $menuId);
                    }
                },
                "productStatusRel"  => function ($query) use ($shopId, $brandId) {
                    $query->where("shopId", $shopId)->where("brandId", $brandId);
                },
                "categoryRel"
            ])->first();
            $status          = $product->status ?? 0;
            $productStatus   = $product->productStatusRel->status ?? 0;
            if (!$product || $status == DBEnum::STATUS_DOWN || $productStatus == DBEnum::STATUS_DOWN) {// 该商品已经下架，请与门店沟通核销或在抖音退款
                throw new ProductException(ProductCodeEnum::ERR_PRODUCT_STATUS_OFF);
            }
            return [
                "product" => $product ? ProductResource::collection([$product])->getIterator()
                                                       ->current() : new \stdClass()
            ];
        } catch (Exception $exception) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_STATUS_OFF);
        }
    }
}
